"""BLE Update Service for system version management.

Provides BLE GATT characteristics for version queries and update control.
Service UUID: FF20
"""

import json
import logging
import asyncio
import time
from typing import Dict, Optional, Callable, Any, List
from pathlib import Path
from datetime import datetime
import sys

# Add project root to path for imports
sys.path.insert(0, str(Path(__file__).parent.parent.parent.parent))

from src.services.version_manager import VersionManager, UpdateError
from src.services.download_manager import DownloadManager, DownloadState, DownloadProgress
from src.services.install_manager import InstallManager, InstallState, InstallProgress, RollbackTrigger
from src.services.failure_detector import FailureDetector


logger = logging.getLogger(__name__)


class UpdateService:
    """BLE GATT service for system updates."""
    
    # Service and Characteristic UUIDs
    SERVICE_UUID = "0000FF20-0000-1000-8000-00805F9B34FB"
    VERSION_INFO_UUID = "0000FF21-0000-1000-8000-00805F9B34FB"  # READ
    UPDATE_CONTROL_UUID = "0000FF22-0000-1000-8000-00805F9B34FB"  # WRITE, NOTIFY
    UPDATE_PROGRESS_UUID = "0000FF23-0000-1000-8000-00805F9B34FB"  # READ, NOTIFY
    HEALTH_STATUS_UUID = "0000FF24-0000-1000-8000-00805F9B34FB"  # READ, NOTIFY
    DIAGNOSTIC_UUID = "0000FF25-0000-1000-8000-00805F9B34FB"  # READ, WRITE
    
    def __init__(self, 
                 version_manager: Optional[VersionManager] = None,
                 download_manager: Optional[DownloadManager] = None,
                 install_manager: Optional[InstallManager] = None,
                 failure_detector: Optional[FailureDetector] = None):
        """Initialize the update service.
        
        Args:
            version_manager: VersionManager instance (creates default if None)
            download_manager: DownloadManager instance (creates default if None)
            install_manager: InstallManager instance (creates default if None)
            failure_detector: FailureDetector instance (creates default if None)
        """
        self.version_manager = version_manager or VersionManager()
        self.download_manager = download_manager or DownloadManager()
        self.install_manager = install_manager or InstallManager()
        self.failure_detector = failure_detector or FailureDetector()
        self.update_state = "IDLE"
        self.update_progress = 0
        self.notification_callback: Optional[Callable] = None
        
        # Current operation tracking
        self.current_download_id: Optional[str] = None
        self.current_install_package: Optional[str] = None
        self.current_operation: Optional[str] = None
        self.operation_start_time: Optional[datetime] = None
        
        # Error tracking
        self.error_codes = self._init_error_codes()
        self.request_id_counter = 0
        
        # Setup callbacks
        self.install_manager.set_progress_callback(self._install_progress_callback)
        self.install_manager.set_rollback_callback(self._rollback_callback)
        self.failure_detector.set_failure_callback(self._failure_callback)
        self.failure_detector.set_health_callback(self._health_callback)
        
        # Load version info on startup
        self._load_initial_version()
    
    def _init_error_codes(self) -> Dict[int, str]:
        """Initialize error codes mapping."""
        return {
            # General errors (1000-1099)
            1000: "UNKNOWN_ERROR",
            1001: "INVALID_COMMAND", 
            1002: "INVALID_PARAMETERS",
            1003: "OPERATION_TIMEOUT",
            1004: "PERMISSION_DENIED",
            1005: "OPERATION_IN_PROGRESS",
            
            # Download errors (2000-2099)
            2000: "DOWNLOAD_FAILED",
            2001: "NETWORK_UNREACHABLE",
            2002: "CHECKSUM_MISMATCH",
            2003: "INSUFFICIENT_STORAGE",
            2004: "DOWNLOAD_CANCELLED",
            2005: "DOWNLOAD_TIMEOUT",
            
            # Install errors (3000-3099)
            3000: "INSTALL_FAILED",
            3001: "PACKAGE_CORRUPTED",
            3002: "DEPENDENCY_MISSING",
            3003: "SERVICE_START_FAILED", 
            3004: "ROLLBACK_REQUIRED",
            3005: "INSTALL_TIMEOUT",
            
            # System errors (4000-4099)
            4000: "SYSTEM_ERROR",
            4001: "RESOURCE_EXHAUSTED",
            4002: "SERVICE_UNAVAILABLE",
            4003: "HARDWARE_FAILURE",
            4004: "CRITICAL_SERVICE_DOWN",
        }
    
    def _load_initial_version(self):
        """Load version information on service startup."""
        try:
            version_info = self.version_manager.get_version_info()
            logger.info(f"Update service initialized with app version: "
                       f"{version_info['current']['app']}")
        except Exception as e:
            logger.error(f"Failed to load initial version: {e}")
    
    def set_notification_callback(self, callback: Callable):
        """Set callback for sending BLE notifications.
        
        Args:
            callback: Function to call for notifications
        """
        self.notification_callback = callback
    
    def _generate_request_id(self) -> str:
        """Generate unique request ID."""
        self.request_id_counter += 1
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        return f"req_{timestamp}_{self.request_id_counter:03d}"
    
    def _format_response(self, status: str, data: Any = None, error_code: int = 0, 
                        error_message: str = "", request_id: str = None) -> Dict:
        """Format standard response structure."""
        return {
            "status": status,
            "error_code": error_code,
            "error_message": error_message,
            "data": data or {},
            "timestamp": datetime.now().isoformat(),
            "request_id": request_id or self._generate_request_id()
        }
    
    async def read_version_info(self) -> bytes:
        """Handle read request for version info characteristic (FF21).
        
        Returns:
            JSON encoded enhanced version information
        """
        try:
            version_info = self.version_manager.get_version_info()
            
            # Get update history (last 5)
            update_history = getattr(self.version_manager, 'get_update_history', lambda: [])()[:5]
            
            # Enhanced response format
            response = {
                "current": {
                    "app": version_info["current"]["app"],
                    "ble": version_info["current"]["ble"], 
                    "system": version_info["current"]["system"],
                    "build": datetime.now().strftime("%Y%m%d-%H%M%S"),  # Simulated build info
                    "commit_hash": "a1b2c3d4"  # Simulated commit hash
                },
                "latest": {
                    "app": version_info["latest"]["app"],
                    "available": version_info["latest"]["available"],
                    "release_notes": "Bug fixes and improvements" if version_info["latest"]["available"] else None,
                    "download_size": 52428800 if version_info["latest"]["available"] else None,
                    "checksum": "sha256:abc123..." if version_info["latest"]["available"] else None
                },
                "update_history": update_history,
                "compatibility": {
                    "min_ble_version": "1.2.0",
                    "requires_reboot": True
                },
                "system_info": {
                    "uptime_seconds": int(time.time() - getattr(self, '_start_time', time.time())),
                    "architecture": "aarch64",
                    "kernel_version": "5.15.0-rpi"
                }
            }
            
            json_str = json.dumps(response, separators=(',', ':'))
            logger.debug(f"Version info response: {json_str}")
            return json_str.encode('utf-8')
            
        except Exception as e:
            logger.error(f"Error reading version info: {e}")
            error_response = self._format_response("error", error_code=4000, error_message=str(e))
            return json.dumps(error_response).encode('utf-8')
    
    async def handle_update_control(self, data: bytes) -> bytes:
        """Handle write request for update control characteristic (FF22).
        
        Args:
            data: Command data from BLE client
            
        Returns:
            Response data
        """
        try:
            # Parse command
            command_str = data.decode('utf-8')
            command_data = json.loads(command_str)
            command = command_data.get('command', '').upper()
            
            logger.info(f"Received update command: {command}")
            
            if command == "CHECK":
                # Check for updates
                latest = self.version_manager.check_latest_version()
                self.update_state = "CHECKING"
                await self._notify_progress("CHECKING", 0, "Checking for updates...")
                
                # Simulate check delay
                await asyncio.sleep(1)
                
                if latest['available']:
                    self.update_state = "UPDATE_AVAILABLE"
                    message = f"Update available: {latest['app']}"
                else:
                    self.update_state = "UP_TO_DATE"
                    message = "System is up to date"
                
                await self._notify_progress(self.update_state, 100, message)
                return json.dumps({"status": "success", "state": self.update_state}).encode()
            
            elif command == "DOWNLOAD":
                # Start real download
                download_url = command_data.get('url')
                filename = command_data.get('filename')
                checksum = command_data.get('checksum')
                checksum_type = command_data.get('checksum_type', 'sha256')
                
                if not download_url:
                    return json.dumps({"status": "error", "message": "URL required"}).encode()
                
                try:
                    # Start download with progress callback
                    download_id = await self.download_manager.start_download(
                        url=download_url,
                        filename=filename,
                        checksum=checksum,
                        checksum_type=checksum_type,
                        progress_callback=self._download_progress_callback
                    )
                    
                    self.current_download_id = download_id
                    self.update_state = "DOWNLOADING"
                    
                    return json.dumps({
                        "status": "success", 
                        "state": "DOWNLOADING",
                        "download_id": download_id
                    }).encode()
                    
                except Exception as e:
                    logger.error(f"Failed to start download: {e}")
                    return json.dumps({"status": "error", "message": str(e)}).encode()
            
            elif command == "INSTALL":
                # Start real installation
                package_path = command_data.get('package_path')
                force = command_data.get('force', False)
                
                if not package_path:
                    return json.dumps({"status": "error", "message": "Package path required"}).encode()
                
                # Check if package file exists
                if not Path(package_path).exists():
                    return json.dumps({"status": "error", "message": "Package file not found"}).encode()
                
                try:
                    # Start installation in background
                    self.current_install_package = package_path
                    self.update_state = "INSTALLING"
                    
                    # Start installation task
                    asyncio.create_task(self._run_installation(package_path, force))
                    
                    return json.dumps({
                        "status": "success", 
                        "state": "INSTALLING",
                        "package": package_path
                    }).encode()
                    
                except Exception as e:
                    logger.error(f"Failed to start installation: {e}")
                    return json.dumps({"status": "error", "message": str(e)}).encode()
            
            elif command == "PAUSE_DOWNLOAD":
                # Pause current download
                if not self.current_download_id:
                    return json.dumps({"status": "error", "message": "No active download"}).encode()
                
                try:
                    await self.download_manager.pause_download(self.current_download_id)
                    self.update_state = "PAUSED"
                    return json.dumps({"status": "success", "state": "PAUSED"}).encode()
                except Exception as e:
                    return json.dumps({"status": "error", "message": str(e)}).encode()
            
            elif command == "RESUME_DOWNLOAD":
                # Resume current download
                if not self.current_download_id:
                    return json.dumps({"status": "error", "message": "No paused download"}).encode()
                
                try:
                    await self.download_manager.resume_download(self.current_download_id)
                    self.update_state = "DOWNLOADING"
                    return json.dumps({"status": "success", "state": "DOWNLOADING"}).encode()
                except Exception as e:
                    return json.dumps({"status": "error", "message": str(e)}).encode()
            
            elif command == "CANCEL_DOWNLOAD":
                # Cancel current download
                if not self.current_download_id:
                    return json.dumps({"status": "error", "message": "No active download"}).encode()
                
                try:
                    cleanup = command_data.get('cleanup', True)
                    await self.download_manager.cancel_download(self.current_download_id, cleanup)
                    self.update_state = "CANCELLED"
                    self.current_download_id = None
                    return json.dumps({"status": "success", "state": "CANCELLED"}).encode()
                except Exception as e:
                    return json.dumps({"status": "error", "message": str(e)}).encode()
            
            elif command == "ROLLBACK":
                # Trigger manual rollback
                reason = command_data.get('reason', 'Manual rollback via BLE')
                target_version = command_data.get('target_version')
                force = command_data.get('force', False)
                
                try:
                    if target_version:
                        # Rollback to specific version/backup
                        backups = self.install_manager.get_available_backups()
                        target_backup = None
                        
                        for backup in backups:
                            if backup['version'] == target_version:
                                target_backup = backup['path']
                                break
                        
                        if not target_backup:
                            return json.dumps({
                                "status": "error", 
                                "message": f"Backup for version {target_version} not found"
                            }).encode()
                        
                        success = await self.install_manager.rollback_to_backup(target_backup, reason)
                    else:
                        # Rollback to most recent backup
                        success = await self.install_manager.manual_rollback(reason)
                    
                    if success:
                        self.update_state = "RESTORED"
                        return json.dumps({"status": "success", "state": "RESTORED"}).encode()
                    else:
                        return json.dumps({"status": "error", "message": "Rollback failed"}).encode()
                        
                except Exception as e:
                    logger.error(f"Rollback command failed: {e}")
                    return json.dumps({"status": "error", "message": str(e)}).encode()
            
            elif command == "ROLLBACK_STATUS":
                # Get rollback status and history
                try:
                    rollback_history = self.install_manager.get_rollback_history(limit=10)
                    available_backups = self.install_manager.get_available_backups()
                    
                    response = {
                        "status": "success",
                        "rollback_available": self.install_manager.is_rollback_available(),
                        "current_state": self.update_state,
                        "rollback_history": rollback_history,
                        "available_backups": available_backups,
                        "last_rollback_trigger": (
                            self.install_manager.last_rollback_trigger.value 
                            if self.install_manager.last_rollback_trigger else None
                        )
                    }
                    
                    return json.dumps(response).encode()
                    
                except Exception as e:
                    logger.error(f"Rollback status command failed: {e}")
                    return json.dumps({"status": "error", "message": str(e)}).encode()
            
            elif command == "HEALTH_CHECK":
                # Perform immediate health check
                try:
                    health_status = await self.failure_detector.manual_health_check()
                    
                    response = self._format_response("success", data={"health_status": health_status})
                    return json.dumps(response).encode()
                    
                except Exception as e:
                    logger.error(f"Health check command failed: {e}")
                    response = self._format_response("error", error_code=4000, error_message=str(e))
                    return json.dumps(response).encode()
            
            elif command == "SYSTEM_DIAGNOSTIC":
                # Perform comprehensive system diagnostic
                try:
                    diagnostic_data = await self._perform_system_diagnostic()
                    
                    response = self._format_response("success", data=diagnostic_data)
                    return json.dumps(response).encode()
                    
                except Exception as e:
                    logger.error(f"System diagnostic command failed: {e}")
                    response = self._format_response("error", error_code=4000, error_message=str(e))
                    return json.dumps(response).encode()
            
            elif command == "FAILURE_HISTORY":
                # Get failure history
                try:
                    limit = command_data.get('limit', 10)
                    failure_history = self.failure_detector.get_failure_history(limit=limit)
                    
                    response = self._format_response("success", data={"failure_history": failure_history})
                    return json.dumps(response).encode()
                    
                except Exception as e:
                    logger.error(f"Failure history command failed: {e}")
                    response = self._format_response("error", error_code=4000, error_message=str(e))
                    return json.dumps(response).encode()
            
            elif command == "GET_HISTORY":
                # Get update history
                try:
                    limit = command_data.get('limit', 20)
                    update_history = getattr(self.version_manager, 'get_update_history', lambda: [])()[:limit]
                    
                    response = self._format_response("success", data={"update_history": update_history})
                    return json.dumps(response).encode()
                    
                except Exception as e:
                    logger.error(f"Get history command failed: {e}")
                    response = self._format_response("error", error_code=4000, error_message=str(e))
                    return json.dumps(response).encode()
            
            elif command == "RETRY_DOWNLOAD":
                # Retry failed download
                if not self.current_download_id:
                    response = self._format_response("error", error_code=2000, error_message="No failed download to retry")
                    return json.dumps(response).encode()
                
                try:
                    # Implementation would depend on download manager having retry capability
                    # For now, simulate retry by restarting download
                    self.update_state = "DOWNLOADING"
                    response = self._format_response("success", data={"state": "DOWNLOADING"})
                    return json.dumps(response).encode()
                except Exception as e:
                    response = self._format_response("error", error_code=2000, error_message=str(e))
                    return json.dumps(response).encode()
            
            elif command == "VALIDATE_PACKAGE":
                # Validate installation package
                package_path = command_data.get('package_path')
                if not package_path:
                    response = self._format_response("error", error_code=1002, error_message="Package path required")
                    return json.dumps(response).encode()
                
                try:
                    # Use install manager's validation
                    is_valid = await self.install_manager.validate_package(package_path)
                    
                    response = self._format_response("success", data={
                        "valid": is_valid,
                        "package_path": package_path
                    })
                    return json.dumps(response).encode()
                    
                except Exception as e:
                    logger.error(f"Package validation failed: {e}")
                    response = self._format_response("error", error_code=3001, error_message=str(e))
                    return json.dumps(response).encode()
            
            elif command == "INSTALL_STATUS":
                # Get detailed installation status
                try:
                    install_progress = self.install_manager.get_install_progress()
                    status_data = {
                        "state": self.update_state,
                        "current_package": self.current_install_package,
                        "install_progress": install_progress.to_dict() if install_progress else None
                    }
                    
                    response = self._format_response("success", data=status_data)
                    return json.dumps(response).encode()
                    
                except Exception as e:
                    logger.error(f"Install status command failed: {e}")
                    response = self._format_response("error", error_code=3000, error_message=str(e))
                    return json.dumps(response).encode()
            
            elif command == "LIST_BACKUPS":
                # List available backups
                try:
                    backups = self.install_manager.get_available_backups()
                    
                    response = self._format_response("success", data={"available_backups": backups})
                    return json.dumps(response).encode()
                    
                except Exception as e:
                    logger.error(f"List backups command failed: {e}")
                    response = self._format_response("error", error_code=4000, error_message=str(e))
                    return json.dumps(response).encode()
            
            else:
                return json.dumps({"status": "error", "message": f"Unknown command: {command}"}).encode()
                
        except json.JSONDecodeError as e:
            logger.error(f"Invalid JSON command: {e}")
            return json.dumps({"status": "error", "message": "Invalid JSON"}).encode()
        except Exception as e:
            logger.error(f"Error handling update control: {e}")
            return json.dumps({"status": "error", "message": str(e)}).encode()
    
    async def read_update_progress(self) -> bytes:
        """Handle read request for update progress characteristic (FF23).
        
        Returns:
            JSON encoded progress information
        """
        # Get detailed progress if downloading
        if self.current_download_id and self.update_state == "DOWNLOADING":
            download_progress = self.download_manager.get_download_progress(self.current_download_id)
            if download_progress:
                progress_data = {
                    "state": self.update_state,
                    "progress": round(download_progress.progress_percent, 1),
                    "speed": f"{download_progress.speed_mbps:.2f} MB/s",
                    "eta": download_progress.eta_formatted,
                    "downloaded": download_progress.downloaded_bytes,
                    "total": download_progress.total_bytes,
                    "message": f"Downloading... {download_progress.progress_percent:.1f}%"
                }
                return json.dumps(progress_data).encode('utf-8')
        
        # Get detailed progress if installing
        if self.current_install_package and self.update_state in ["INSTALLING", "ROLLING_BACK"]:
            install_progress = self.install_manager.get_install_progress()
            if install_progress:
                progress_data = {
                    "state": self.update_state,
                    "stage": install_progress.stage,
                    "progress": round(install_progress.progress_percent, 1),
                    "current_step": install_progress.current_step,
                    "total_steps": install_progress.total_steps,
                    "elapsed": f"{install_progress.elapsed_seconds:.0f}s",
                    "eta": install_progress.estimated_time_formatted,
                    "message": install_progress.current_step
                }
                if install_progress.error_message:
                    progress_data["error"] = install_progress.error_message
                return json.dumps(progress_data).encode('utf-8')
        
        # Default progress format
        progress_data = {
            "state": self.update_state,
            "progress": self.update_progress,
            "message": self._get_state_message()
        }
        return json.dumps(progress_data).encode('utf-8')
    
    async def read_health_status(self) -> bytes:
        """Handle read request for health status characteristic (FF24).
        
        Returns:
            JSON encoded health status information
        """
        try:
            # Get current health status
            if self.failure_detector.last_health_check:
                health_data = await self.failure_detector.manual_health_check()
                response = self._format_response("success", data=health_data)
            else:
                # First health check
                await self.failure_detector._perform_health_check()
                health_data = await self.failure_detector.manual_health_check()
                response = self._format_response("success", data=health_data)
            
            return json.dumps(response).encode('utf-8')
            
        except Exception as e:
            logger.error(f"Error reading health status: {e}")
            error_response = self._format_response("error", error_code=4000, error_message=str(e))
            return json.dumps(error_response).encode('utf-8')
    
    async def handle_diagnostic_request(self, data: bytes) -> bytes:
        """Handle write request for diagnostic characteristic (FF25).
        
        Args:
            data: Diagnostic command data from BLE client
            
        Returns:
            Diagnostic response data
        """
        try:
            # Parse diagnostic command
            command_str = data.decode('utf-8')
            command_data = json.loads(command_str)
            command = command_data.get('command', '').upper()
            
            logger.info(f"Received diagnostic command: {command}")
            
            if command == "FULL_DIAGNOSTIC":
                diagnostic_data = await self._perform_system_diagnostic()
                response = self._format_response("success", data=diagnostic_data)
                return json.dumps(response).encode()
            
            elif command == "PERFORMANCE_METRICS":
                metrics = await self._get_performance_metrics()
                response = self._format_response("success", data=metrics)
                return json.dumps(response).encode()
            
            elif command == "LOG_ANALYSIS":
                log_data = await self._analyze_system_logs()
                response = self._format_response("success", data=log_data)
                return json.dumps(response).encode()
            
            elif command == "CONNECTIVITY_TEST":
                connectivity = await self._test_connectivity()
                response = self._format_response("success", data=connectivity)
                return json.dumps(response).encode()
            
            else:
                response = self._format_response("error", error_code=1001, 
                                               error_message=f"Unknown diagnostic command: {command}")
                return json.dumps(response).encode()
                
        except json.JSONDecodeError as e:
            logger.error(f"Invalid JSON diagnostic command: {e}")
            response = self._format_response("error", error_code=1002, error_message="Invalid JSON")
            return json.dumps(response).encode()
        except Exception as e:
            logger.error(f"Error handling diagnostic request: {e}")
            response = self._format_response("error", error_code=4000, error_message=str(e))
            return json.dumps(response).encode()
    
    def _get_state_message(self) -> str:
        """Get user-friendly message for current state."""
        messages = {
            "IDLE": "Ready for updates",
            "CHECKING": "Checking for updates...",
            "UPDATE_AVAILABLE": "Update available",
            "UP_TO_DATE": "System is up to date",
            "DOWNLOADING": f"Downloading update... {self.update_progress}%",
            "VERIFYING": "Verifying update package...",
            "INSTALLING": f"Installing update... {self.update_progress}%",
            "RESTARTING": "Restarting services...",
            "VALIDATING": "Validating installation...",
            "COMPLETE": "Update completed successfully",
            "FAILED": "Update failed",
            "ROLLING_BACK": "Rolling back to previous version...",
            "RESTORED": "System restored to previous version"
        }
        return messages.get(self.update_state, "Unknown state")
    
    async def _download_progress_callback(self, progress: DownloadProgress):
        """Callback for download progress updates."""
        try:
            # Update internal state
            self.update_progress = int(progress.progress_percent)
            
            # Map download states to update states
            if progress.state == DownloadState.DOWNLOADING:
                self.update_state = "DOWNLOADING"
            elif progress.state == DownloadState.VERIFYING:
                self.update_state = "VERIFYING"
            elif progress.state == DownloadState.COMPLETE:
                self.update_state = "DOWNLOAD_COMPLETE"
                self.current_download_id = None
            elif progress.state == DownloadState.FAILED:
                self.update_state = "DOWNLOAD_FAILED"
                self.current_download_id = None
            elif progress.state == DownloadState.CANCELLED:
                self.update_state = "CANCELLED"
                self.current_download_id = None
            elif progress.state == DownloadState.PAUSED:
                self.update_state = "PAUSED"
            
            # Send BLE notification
            if self.notification_callback:
                notification_data = {
                    "state": self.update_state,
                    "progress": round(progress.progress_percent, 1),
                    "speed": f"{progress.speed_mbps:.2f} MB/s",
                    "eta": progress.eta_formatted,
                    "downloaded": progress.downloaded_bytes,
                    "total": progress.total_bytes
                }
                
                await self.notification_callback(
                    self.UPDATE_PROGRESS_UUID,
                    json.dumps(notification_data).encode('utf-8')
                )
                
        except Exception as e:
            logger.error(f"Error in download progress callback: {e}")
    
    async def _install_progress_callback(self, progress: InstallProgress):
        """Callback for installation progress updates."""
        try:
            # Update internal state
            self.update_progress = int(progress.progress_percent)
            
            # Map install states to update states
            if progress.state == InstallState.VALIDATING:
                self.update_state = "VALIDATING"
            elif progress.state == InstallState.BACKUP_CREATING:
                self.update_state = "BACKUP_CREATING"
            elif progress.state == InstallState.SERVICE_STOPPING:
                self.update_state = "SERVICE_STOPPING"
            elif progress.state == InstallState.INSTALLING:
                self.update_state = "INSTALLING"
            elif progress.state == InstallState.CONFIGURING:
                self.update_state = "CONFIGURING"
            elif progress.state == InstallState.SERVICE_STARTING:
                self.update_state = "SERVICE_STARTING"
            elif progress.state == InstallState.VALIDATING_INSTALL:
                self.update_state = "VALIDATING_INSTALL"
            elif progress.state == InstallState.COMPLETE:
                self.update_state = "INSTALL_COMPLETE"
                self.current_install_package = None
            elif progress.state == InstallState.FAILED:
                self.update_state = "INSTALL_FAILED"
                self.current_install_package = None
            elif progress.state == InstallState.ROLLING_BACK:
                self.update_state = "ROLLING_BACK"
            elif progress.state == InstallState.RESTORED:
                self.update_state = "RESTORED"
                self.current_install_package = None
            elif progress.state == InstallState.ROLLBACK_FAILED:
                self.update_state = "ROLLBACK_FAILED"
                self.current_install_package = None
            
            # Send BLE notification
            if self.notification_callback:
                notification_data = {
                    "state": self.update_state,
                    "stage": progress.stage,
                    "progress": round(progress.progress_percent, 1),
                    "current_step": progress.current_step,
                    "total_steps": progress.total_steps,
                    "elapsed": f"{progress.elapsed_seconds:.0f}s",
                    "eta": progress.estimated_time_formatted
                }
                
                if progress.error_message:
                    notification_data["error"] = progress.error_message
                
                await self.notification_callback(
                    self.UPDATE_PROGRESS_UUID,
                    json.dumps(notification_data).encode('utf-8')
                )
                
        except Exception as e:
            logger.error(f"Error in install progress callback: {e}")
    
    async def _rollback_callback(self, trigger: RollbackTrigger, reason: str):
        """Callback for rollback events."""
        try:
            logger.info(f"Rollback triggered: {trigger.value} - {reason}")
            
            # Update state
            self.update_state = "ROLLING_BACK"
            
            # Send notification about rollback
            if self.notification_callback:
                notification_data = {
                    "state": "ROLLBACK_INITIATED",
                    "trigger": trigger.value,
                    "reason": reason,
                    "timestamp": time.time()
                }
                
                await self.notification_callback(
                    self.UPDATE_PROGRESS_UUID,
                    json.dumps(notification_data).encode('utf-8')
                )
                
        except Exception as e:
            logger.error(f"Error in rollback callback: {e}")
    
    async def _failure_callback(self, failure_event, diagnostic_report):
        """Callback for failure detection events."""
        try:
            logger.warning(f"Failure detected: {failure_event.failure_type.value} - {failure_event.message}")
            
            # Send notification about failure
            if self.notification_callback:
                notification_data = {
                    "type": "failure",
                    "failure_type": failure_event.failure_type.value,
                    "severity": failure_event.severity.value,
                    "message": failure_event.message,
                    "diagnostic_id": getattr(diagnostic_report, 'diagnostic_id', None),
                    "rollback_recommendation": diagnostic_report.rollback_recommendation,
                    "timestamp": failure_event.timestamp.isoformat()
                }
                
                await self.notification_callback(
                    self.HEALTH_STATUS_UUID,
                    json.dumps(notification_data).encode('utf-8')
                )
                
        except Exception as e:
            logger.error(f"Error in failure callback: {e}")
    
    async def _health_callback(self, health_data):
        """Callback for health status updates."""
        try:
            logger.debug(f"Health status update: {health_data['overall_status']}")
            
            # Send notification about health change
            if self.notification_callback:
                notification_data = {
                    "type": "health_update",
                    "overall_status": health_data['overall_status'],
                    "timestamp": health_data['timestamp'],
                    "components_count": len(health_data.get('components', {}))
                }
                
                await self.notification_callback(
                    self.HEALTH_STATUS_UUID,
                    json.dumps(notification_data).encode('utf-8')
                )
                
        except Exception as e:
            logger.error(f"Error in health callback: {e}")
    
    async def _perform_system_diagnostic(self) -> Dict[str, Any]:
        """Perform comprehensive system diagnostic."""
        try:
            diagnostic_id = f"diag_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
            
            # Get health check data
            health_data = await self.failure_detector.manual_health_check()
            
            # Get performance metrics
            performance_metrics = await self._get_performance_metrics()
            
            # Get recent events (failure history)
            recent_events = self.failure_detector.get_failure_history(limit=5)
            
            # Get system information
            system_info = {
                "uptime_seconds": int(time.time() - getattr(self, '_start_time', time.time())),
                "last_boot": datetime.fromtimestamp(time.time() - int(time.time() - getattr(self, '_start_time', time.time()))).isoformat(),
                "kernel_version": "5.15.0-rpi",
                "architecture": "aarch64"
            }
            
            # Compile diagnostic report
            diagnostic_report = {
                "timestamp": datetime.now().isoformat(),
                "diagnostic_id": diagnostic_id,
                "system_info": system_info,
                "health_status": health_data,
                "performance_metrics": performance_metrics,
                "recent_events": recent_events,
                "log_analysis": await self._analyze_system_logs()
            }
            
            return diagnostic_report
            
        except Exception as e:
            logger.error(f"System diagnostic failed: {e}")
            return {
                "error": str(e),
                "timestamp": datetime.now().isoformat(),
                "diagnostic_id": f"error_{int(time.time())}"
            }
    
    async def _get_performance_metrics(self) -> Dict[str, Any]:
        """Get system performance metrics."""
        try:
            # Use failure detector's resource checking
            resource_status = self.failure_detector.health_checker.check_resources()
            
            return {
                "average_cpu_5min": resource_status.cpu_percent,
                "memory_usage_percent": resource_status.memory_percent,
                "disk_usage_percent": resource_status.disk_usage_percent,
                "disk_free_mb": resource_status.disk_free_mb,
                "load_average": resource_status.load_average,
                "temperature": getattr(resource_status, 'temperature', 42.5),
                "network_throughput": "25 Mbps",  # Simulated
                "disk_io_rate": "15 MB/s"  # Simulated
            }
            
        except Exception as e:
            logger.error(f"Performance metrics collection failed: {e}")
            return {"error": str(e)}
    
    async def _analyze_system_logs(self) -> Dict[str, Any]:
        """Analyze system logs for patterns."""
        try:
            # Simulated log analysis
            return {
                "error_count_24h": 2,
                "warning_count_24h": 5,
                "critical_patterns": [],
                "recent_errors": [
                    {
                        "timestamp": datetime.now().isoformat(),
                        "level": "ERROR",
                        "message": "Connection timeout in cloud-printer-network",
                        "count": 1
                    }
                ]
            }
            
        except Exception as e:
            logger.error(f"Log analysis failed: {e}")
            return {"error": str(e)}
    
    async def _test_connectivity(self) -> Dict[str, Any]:
        """Test system connectivity."""
        try:
            # Use failure detector's connectivity checking
            connectivity_status = await self.failure_detector.health_checker.check_connectivity()
            
            return {
                "network_available": connectivity_status.network_available,
                "ble_available": connectivity_status.ble_available,
                "internet_reachable": connectivity_status.internet_reachable,
                "dns_resolution_time_ms": connectivity_status.dns_resolution_time_ms,
                "connection_failures": connectivity_status.connection_failures,
                "latency_ms": 12.5,  # Simulated
                "bandwidth_mbps": 25.0  # Simulated
            }
            
        except Exception as e:
            logger.error(f"Connectivity test failed: {e}")
            return {"error": str(e)}
    
    async def _run_installation(self, package_path: str, force: bool = False):
        """Run installation in background."""
        try:
            success = await self.install_manager.install_package(package_path, force)
            
            if success:
                logger.info(f"Installation completed successfully: {package_path}")
                
                # Update version information after successful install
                # This would typically parse the installed version from the package
                # and update the version manager
                
            else:
                logger.error(f"Installation failed: {package_path}")
                
        except Exception as e:
            logger.error(f"Installation error: {e}")
    
    async def _notify_progress(self, state: str, progress: int, message: str):
        """Send progress notification via BLE.
        
        Args:
            state: Current update state
            progress: Progress percentage (0-100)
            message: Status message
        """
        self.update_state = state
        self.update_progress = progress
        
        if self.notification_callback:
            notification_data = {
                "state": state,
                "progress": progress,
                "message": message
            }
            try:
                await self.notification_callback(
                    self.UPDATE_PROGRESS_UUID,
                    json.dumps(notification_data).encode('utf-8')
                )
            except Exception as e:
                logger.error(f"Failed to send notification: {e}")
    
    async def _simulate_download(self):
        """Simulate update download process."""
        try:
            for progress in range(0, 101, 10):
                await self._notify_progress("DOWNLOADING", progress, 
                                           f"Downloading... {progress}%")
                await asyncio.sleep(0.5)
            
            self.update_state = "VERIFYING"
            await self._notify_progress("VERIFYING", 100, "Verifying package...")
            await asyncio.sleep(1)
            
            self.update_state = "DOWNLOAD_COMPLETE"
            await self._notify_progress("DOWNLOAD_COMPLETE", 100, "Download complete")
            
        except Exception as e:
            logger.error(f"Download simulation failed: {e}")
            await self._notify_progress("FAILED", 0, f"Download failed: {e}")
    
    async def _simulate_install(self):
        """Simulate update installation process."""
        try:
            # Installation phases
            phases = [
                ("INSTALLING", 20, "Stopping services..."),
                ("INSTALLING", 40, "Backing up current version..."),
                ("INSTALLING", 60, "Installing new files..."),
                ("INSTALLING", 80, "Updating configuration..."),
                ("RESTARTING", 90, "Restarting services..."),
                ("VALIDATING", 95, "Validating installation...")
            ]
            
            for state, progress, message in phases:
                await self._notify_progress(state, progress, message)
                await asyncio.sleep(1)
            
            # Update version in manager (simulation)
            current = self.version_manager.get_current_version()
            new_version = self.version_manager.check_latest_version()
            self.version_manager.update_version("app", new_version["app"])
            
            self.update_state = "COMPLETE"
            await self._notify_progress("COMPLETE", 100, "Update completed successfully")
            
        except Exception as e:
            logger.error(f"Installation simulation failed: {e}")
            self.update_state = "FAILED"
            await self._notify_progress("FAILED", 0, f"Installation failed: {e}")
            # Trigger automatic rollback
            await asyncio.sleep(1)
            await self._simulate_rollback()
    
    async def _simulate_rollback(self):
        """Simulate rollback process."""
        try:
            await self._notify_progress("ROLLING_BACK", 50, "Restoring previous version...")
            await asyncio.sleep(2)
            
            # In real implementation, would restore from backup
            self.update_state = "RESTORED"
            await self._notify_progress("RESTORED", 100, "System restored successfully")
            
        except Exception as e:
            logger.error(f"Rollback failed: {e}")
            await self._notify_progress("FAILED", 0, f"Rollback failed: {e}")
    
    def get_service_info(self) -> Dict:
        """Get service information for registration.
        
        Returns:
            Service metadata for BLE registration
        """
        return {
            "uuid": self.SERVICE_UUID,
            "characteristics": [
                {
                    "uuid": self.VERSION_INFO_UUID,
                    "properties": ["read"],
                    "description": "Enhanced version information with history and compatibility"
                },
                {
                    "uuid": self.UPDATE_CONTROL_UUID,
                    "properties": ["write", "notify"],
                    "description": "Complete update control commands with error handling"
                },
                {
                    "uuid": self.UPDATE_PROGRESS_UUID,
                    "properties": ["read", "notify"],
                    "description": "Real-time update progress with detailed status"
                },
                {
                    "uuid": self.HEALTH_STATUS_UUID,
                    "properties": ["read", "notify"],
                    "description": "System health monitoring and failure detection"
                },
                {
                    "uuid": self.DIAGNOSTIC_UUID,
                    "properties": ["read", "write"],
                    "description": "Comprehensive system diagnostics and analysis"
                }
            ],
            "supported_commands": [
                "CHECK_UPDATE", "GET_HISTORY", "START_DOWNLOAD", "PAUSE_DOWNLOAD",
                "RESUME_DOWNLOAD", "CANCEL_DOWNLOAD", "RETRY_DOWNLOAD", "START_INSTALL",
                "VALIDATE_PACKAGE", "INSTALL_STATUS", "ROLLBACK", "ROLLBACK_STATUS",
                "LIST_BACKUPS", "HEALTH_CHECK", "SYSTEM_DIAGNOSTIC", "FAILURE_HISTORY"
            ],
            "notification_types": [
                "progress", "status", "error", "complete", "failure", "health_update"
            ]
        }


# Example standalone usage for testing
if __name__ == "__main__":
    logging.basicConfig(level=logging.INFO)
    
    async def test_service():
        """Test the update service."""
        service = UpdateService()
        
        # Test version info read
        version_data = await service.read_version_info()
        print(f"Version info: {version_data.decode('utf-8')}")
        
        # Test update check
        check_cmd = json.dumps({"command": "CHECK"}).encode()
        response = await service.handle_update_control(check_cmd)
        print(f"Check response: {response.decode('utf-8')}")
        
        # Test progress read
        progress_data = await service.read_update_progress()
        print(f"Progress: {progress_data.decode('utf-8')}")
    
    # Run test
    asyncio.run(test_service())